using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;

namespace DomainTaskTrain;

public class DomainTask
{
    /// <summary>
    /// 异步锁
    /// </summary>
    readonly Lock _lock = new();
    /// <summary>
    /// 任务是否完成
    /// </summary>
    bool _complete = false;
    /// <summary>
    /// 执行任务时出现的异常
    /// </summary>
    Exception? _exception = null;
    /// <summary>
    /// 同线程执行任务
    /// </summary>
    Action? _action = null;
    /// <summary>
    /// 上下文
    /// </summary>
    ExecutionContext? _context = null;

    public bool IsComplete
    {
        get
        {
            lock (_lock)
            {
                return _complete;
            }
        }
    }

    public DomainTaskAwaiter GetAwaiter() => new(this);

    /// <summary>
    /// 异步执行函数
    /// </summary>
    /// <param name="action"></param>
    /// <returns></returns>
    public static DomainTask Run(Action action)
    {
        DomainTask task = new();
        ThreadPool.QueueUserWorkItem(_ =>
        {
            try
            {
                action();
                task.ChangeResult();
            }
            catch (Exception e)
            {
                task.ChangeException(e);
                throw;
            }
        });
        return task;
    }

    /// <summary>
    /// 同线程执行任务
    /// </summary>
    /// <param name="action"></param>
    public void ContinueWith(Action action)
    {
        DomainTask task = new();
        if (_complete)
        {
            ThreadPool.QueueUserWorkItem(_ =>
            {
                try
                {
                    action();
                    task.ChangeResult();
                }
                catch (Exception e)
                {
                    task.ChangeException(e);
                    throw;
                }
            });
        }
        else
        {
            _action = action;
            _context = ExecutionContext.Capture();
        }
    }

    /// <summary>
    /// 等待当前任务完成
    /// </summary>
    public void Wait()
    {
        // 这个类主要用于阻止当前线程
        // 可以用于线程间通信
        // 默认为无信号状态
        // Set方法会传递信号
        // Wait方法为等待接收信号，接收到信号后会继续执行线程中剩下操作
        // Reset方法会重置信号状态
        ManualResetEventSlim? resetEvent = null;

        lock (_lock)
        {
            if (!_complete)
            {
                resetEvent = new();
                ContinueWith(() => resetEvent.Set());
            }
        }

        resetEvent?.Wait();

        if (_exception is not null)
        {
            ExceptionDispatchInfo.Throw(_exception);
        }
    }

    /// <summary>
    /// 延迟指定时间
    /// </summary>
    /// <param name="time"></param>
    /// <returns></returns>
    public static DomainTask Delay(TimeSpan time)
    {
        DomainTask task = new();
        Timer timer = new(_ => task.ChangeResult());
        timer.Change(time, Timeout.InfiniteTimeSpan);
        return task;
    }

    /// <summary>
    /// 将任务状态修改为完成
    /// </summary>
    /// <exception cref="InvalidOperationException"></exception>
    public void ChangeResult() => ChangeState(null);

    /// <summary>
    /// 记录执行任务时触发的异常
    /// </summary>
    /// <param name="e"></param>
    /// <exception cref="InvalidOperationException"></exception>
    public void ChangeException(Exception e) => ChangeState(e);

    /// <summary>
    /// 修改任务状态
    /// </summary>
    /// <param name="e"></param>
    /// <exception cref="InvalidOperationException"></exception>
    public void ChangeState(Exception? e)
    {
        lock (_lock)
        {
            if (_complete)
                throw new InvalidOperationException("task is completed");
            if (_exception is not null)
                throw new InvalidOperationException("there is an exception");
            _exception = e;
            _complete = true;

            // 任务中如果后续同线程的任务就执行
            if (_action is not null)
            {
                if (_context is null)
                {
                    _action?.Invoke();
                }
                else
                {
                    ExecutionContext.Run(_context, state => ((Action?)state)?.Invoke(), _action);
                }
            }
        }
    }
}

/// <summary>
/// 异步调用
/// 继承 INotifyCompletion：必须实现 OnCompleted方法，要有IsCompleted属性和GetResult方法
/// 继承 INotifyCompletion 接口的类型可以使用 await 关键字
/// </summary>
public readonly struct DomainTaskAwaiter : INotifyCompletion
{
    readonly DomainTask? _task = null;

    public bool IsCompleted => _task?.IsComplete ?? false;

    internal DomainTaskAwaiter(DomainTask task) => _task = task;

    public void OnCompleted(Action continuation) => _task?.ContinueWith(continuation);

    public void GetResult() => _task?.Wait();
}
